/*
 *  @(#)RepositoryImpl.java    0.3 16/04/2009
 */
package org.miao.example.repository;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.miao.catstorage.Result;
import org.miao.catstorage.Storage;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * 持久化仓储对象，实现了Repository接口
 * @author Miao
 * @version 0.1
 * @since 0.1
 */
@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public class RepositoryImpl<T> extends JpaDaoSupport implements Repository<T> {

    /**
     * 预定义语句库
     */
    @Resource
    private Storage storage;

    @Transactional(readOnly = false)
    public void persist(T entity) {
        getJpaTemplate().persist(entity);
    }

    @Transactional(readOnly = false)
    public <T> T merge(T entity) {
        return getJpaTemplate().merge(entity);

    }

    @Transactional(readOnly = false)
    public void remove(T entity) {
        getJpaTemplate().remove(entity);
    }

    @Transactional(readOnly = false)
    public void remove(Class entityClass, Object id) {
        getJpaTemplate().remove(find(entityClass, id));
    }

    public boolean contains(T entity) {
        return getJpaTemplate().contains(entity);
    }

    @Transactional(readOnly = false)
    public void refresh(T entity) {
        getJpaTemplate().refresh(entity);
    }

    @Transactional(readOnly = false)
    public void flush() {
        getJpaTemplate().flush();
    }

    @Transactional(readOnly = false)
    public void clear() {
        getJpaTemplate().getEntityManager().clear();
    }

    public T find(Class entityClass, Object id) {
        return (T) getJpaTemplate().find(entityClass, id);
    }

    public List<T> findByByNamedQuery(String name) {
        return getJpaTemplate().findByNamedQuery(name);
    }

    public List<T> findByByNamedQuery(String name, Object... parameters) {
        return getJpaTemplate().findByNamedQuery(name, parameters);
    }

    public List<T> findByNamed(String name, Map<String, Object> parameters, List<String> orders) {
        Result statement = storage.get(name, parameters);
        if (statement != null) {
            for (String order : orders) {
                statement.addOrder(order);
            }
            return query(statement.toString(), parameters);
        } else {
            throw new PersistenceException("没有找到指定的预定义语句");
        }
    }

    public Object statistics(String name, statisticType type, Map<String, Object> parameters) {
        Result statement = storage.get(name, parameters);
        if (statement != null) {
            String statisticStatement = null;
            switch (type) {
                case count:
                    statisticStatement = statement.count();
                    break;
                case distinct:
                    statisticStatement = statement.distinct();
                    break;
                case countDistinct:
                    statisticStatement = statement.countDistinct();
                    break;
                case min:
                    statisticStatement = statement.min();
                    break;
                case max:
                    statisticStatement = statement.max();
                    break;
                case sum:
                    statisticStatement = statement.sum();
                    break;
                case avg:
                    statisticStatement = statement.avg();
                    break;
            }
            return queryStatistics(statisticStatement, parameters);
        } else {
            throw new PersistenceException("没有找到指定的预定义语句");

        }
    }

    public PageBean<T> findPageByNamed(String name, Map<String, Object> parameters, List<String> orders, int currentPage, int pageSize) {
        Result statement = storage.get(name, parameters);
        if (statement != null) {
            int first = PageBean.getFirstResult(currentPage, pageSize);
            long totalResult = (Long) queryStatistics(statement.count(), parameters);
            for (String order : orders) {
                statement.addOrder(order);
            }
            return new PageBean<T>(currentPage, pageSize, totalResult, query(statement.toString(), parameters, first, pageSize));
        } else {
            throw new PersistenceException("没有找到指定的预定义语句");
        }
    }

    /**
     * 查询
     * @param statenemt 查询语句
     * @param parameters 参数
     * @return 查询结果
     * @since 0.3
     */
    private List<T> query(final String statenemt, final Map<String, Object> parameters) {
        return getJpaTemplate().executeFind(
                new JpaCallback() {

                    public Object doInJpa(EntityManager arg0) throws PersistenceException {
                        Query query = arg0.createQuery(statenemt);
                        Iterator<Entry<String, Object>> iterator = parameters.entrySet().iterator();
                        while (iterator.hasNext()) {
                            Entry<String, Object> entry = iterator.next();
                            if (entry.getValue() != null) {
                                query.setParameter(clearSymbol(entry.getKey()), entry.getValue());
                            }
                        }
                        return query.getResultList();
                    }
                });
    }

    /**
     * 分页查询
     * @param statenemt 查询语句
     * @param parameters 参数
     * @param first 起始记录
     * @param pageSize 单页记录数量
     * @return 查询结果
     * @since 0.3
     */
    private List<T> query(final String statenemt, final Map<String, Object> parameters, final int first, final int pageSize) {
        return getJpaTemplate().executeFind(
                new JpaCallback() {

                    public Object doInJpa(EntityManager arg0) throws PersistenceException {
                        Query query = arg0.createQuery(statenemt);
                        Iterator<Entry<String, Object>> iterator = parameters.entrySet().iterator();
                        while (iterator.hasNext()) {
                            Entry<String, Object> entry = iterator.next();
                            if (entry.getValue() != null) {
                                query.setParameter(clearSymbol(entry.getKey()), entry.getValue());
                            }
                        }
                        return query.setFirstResult(first).setMaxResults(pageSize).getResultList();
                    }
                });
    }

    /**
     * 统计查询
     * @param statStatenemt 统计查询语句
     * @param parameters 参数
     * @return 查询结果
     * @since 0.3
     */
    private Object queryStatistics(final String statStatenemt, final Map<String, Object> parameters) {
        return getJpaTemplate().executeFind(
                new JpaCallback() {

                    public Object doInJpa(EntityManager arg0) throws PersistenceException {
                        Query query = arg0.createQuery(statStatenemt);
                        Iterator<Entry<String, Object>> iterator = parameters.entrySet().iterator();
                        while (iterator.hasNext()) {
                            Entry<String, Object> entry = iterator.next();
                            if (entry.getValue() != null) {
                                query.setParameter(clearSymbol(entry.getKey()), entry.getValue());
                            }
                        }
                        return query.getSingleResult();
                    }
                });
    }

    /**
     * 去除参数上的预定义符号
     * @param param 参数
     * @return 处理好的参数
     * @since 0.3
     */
    private String clearSymbol(String param) {
        return param.replaceAll("^\\:", "");
    }

    @Transactional(readOnly = false)
    public void lockByRead(T entity) {
        getJpaTemplate().getEntityManager().lock(entity, LockModeType.READ);
    }

    @Transactional(readOnly = false)
    public void lockByWrite(T entity) {
        getJpaTemplate().getEntityManager().lock(entity, LockModeType.WRITE);
    }

    /**
     * 获得预定义语句库
     * @return 预定义语句库
     * @since 0.3
     */
    public Storage getStorage() {
        return storage;
    }

    /**
     * 设置预定义语句库
     * @param storage 预定义语句库
     * @since 0.3
     */
    public void setStorage(Storage storage) {
        this.storage = storage;
    }
}
